package com.ymm.websocket.demo.server;


import com.ymm.driver.api.constants.Entity.DriverEntity;
import com.ymm.lulutong.dto.OrderShipperDTO;
import com.ymm.websocket.demo.model.RoleInfo;
import com.ymm.websocket.demo.rpc.DriverRPC;
import com.ymm.websocket.demo.rpc.ShipperRPC;
import com.ymm.websocket.demo.utils.enums.RoleEnum;
import com.ymm.websocket.demo.utils.enums.StateEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description: WebSocketServer
 * 定义WebSocket server（多例的，多线程的）
 * roleType表示角色类型,0表示客服，1表示用户
 * @Author: Xuecheng Li
 * @CreateTime: 2022-08-10 10:53
 * @Version: 1.0.0
 */
@Slf4j
@ServerEndpoint("/api/websocketServiceHashMap2/{role}/{id}")
@Component
public class WebSocketServerCustomerHashMap2 {

    /**
     * 静态变量，用来记录当前在线人数（司机和客服的信息）
     */
    private static AtomicInteger onlineCustomerCount = new AtomicInteger(0);
    private static AtomicInteger onlineServiceCount = new AtomicInteger(0);

    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。(不用重写WebSocketServer，直接判断是否同一个对象就行)
     */
    private static Map<Session, WebSocketServerCustomerHashMap2> waitingCustomerMap = new ConcurrentHashMap<>();
    private static List<Session> waitingCustomerList = new CopyOnWriteArrayList<>();

    private static Map<Session, WebSocketServerCustomerHashMap2> availableServiceMap = new ConcurrentHashMap<>();

    private static List<Session> availableServiceList = new CopyOnWriteArrayList<>();


    /**
     * 记录通话中客服
     */
    private static Map<Session, WebSocketServerCustomerHashMap2> busyServiceMap = new ConcurrentHashMap<>();
    private static List<Session> busyServiceList = new ArrayList<>();

    /**
     * 记录通话中客户
     */
    private static Map<Session, WebSocketServerCustomerHashMap2> busyCustomerMap = new ConcurrentHashMap<>();
    private static List<Session> busyCustomerList = new CopyOnWriteArrayList<>();


    /**
     * 保存角色核心信息(存在并发安全问题)
     */
    private RoleInfo roleInfo = new RoleInfo();


    private static ShipperRPC shipperRPC;
    @Resource
    public void setShipperRPC(ShipperRPC shipperRPC) {
        WebSocketServerCustomerHashMap2.shipperRPC = shipperRPC;
    }


    private static DriverRPC driverRPC;
    @Resource
    public void setDriverRPC(DriverRPC driverRPC) {
        WebSocketServerCustomerHashMap2.driverRPC = driverRPC;
    }

    @OnOpen
    public void onOpen (Session session, @PathParam("role") Integer role, @PathParam("id") Long id) {


        //1.记录登录身份信息
        roleInfo.setSession(session);
        roleInfo.setRole(role);
        roleInfo.setState(0);
        roleInfo.setId(id);


        //2.判断角色
        if (role.equals(RoleEnum.CUSTOMER_SERVICE.getCode())) {
            //2.1 客服
            //2.1.1 添加姓名信息
            roleInfo.setRoleName("路路通客服一号");

            //2.2 添加客服池
            onlineServiceCount.getAndIncrement();
            availableServiceList.add(session);
            availableServiceMap.put(session, this);

            searchOnlineCustomer(session, this);
        } else {

            // 2.2 客户
            //2.2.1 司机
            if (role.equals(RoleEnum.DRIVER.getCode())) {
                DriverEntity driver = driverRPC.getDriverInfoById(id);
                roleInfo.setRoleName(driver.getDriverName());

            } //2.2.2 货主
            else {
                OrderShipperDTO shipperInfo = shipperRPC.getShipperInfo(id);
                roleInfo.setRoleName(shipperInfo.getNickname());
            }

            onlineCustomerCount.getAndIncrement();
            waitingCustomerList.add(session);
            waitingCustomerMap.put(session, this);

            searchOnlineService(session, this);

        }

    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //判断是用户还是客服
        if (roleInfo.getRole().equals(RoleEnum.CUSTOMER_SERVICE.getCode())) {
            // 处理客服未结束服务关闭会话的异常 TODO

            // 繁忙状态关闭
            if (roleInfo.getState().equals(StateEnum.BUSY.getCode())) {
                //客户解除与客服连接，并发送提示消息
                Session bindingUserSession = roleInfo.getBandingRoleSession();
                WebSocketServerCustomerHashMap2 bindingUserWebSocket = busyCustomerMap.get(bindingUserSession);

                bindingUserWebSocket.roleInfo.setState(StateEnum.AVAILABLE.getCode());
                bindingUserWebSocket.roleInfo.setBandingRoleSession(null);

                //用户进入等待池，并解除繁忙池
                waitingCustomerMap.put(bindingUserSession, bindingUserWebSocket);
                waitingCustomerList.add(bindingUserSession);
                onlineCustomerCount.getAndIncrement();

                busyCustomerMap.remove(bindingUserSession);
                busyCustomerList.remove(bindingUserSession);

                log.info("连接中的客服意外离开");

                //给客户发送消息：“无空闲客服，请等待”
                try {
                    sendMessageTarget("客服意外离开，请等待", bindingUserSession);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                //解除繁忙客服池
                busyServiceList.remove(roleInfo.getSession());
                busyServiceMap.remove(roleInfo.getSession());

                //立即为客户寻找空闲客服
                searchOnlineService(bindingUserSession, bindingUserWebSocket);

            } else { // 空闲状态关闭

                onlineServiceCount.getAndDecrement();
                availableServiceList.remove(roleInfo.getSession());
                availableServiceMap.remove(roleInfo.getSession());
            }

        } else {

            //解除客户与客服的绑定，释放客服
            if (roleInfo.getState().equals(StateEnum.BUSY.getCode())) {
                //解除连接，释放客服，并给客服提示消息
                Session customerServiceSession = roleInfo.getBandingRoleSession();
                WebSocketServerCustomerHashMap2 serverCustomerWebSocket = busyServiceMap.get(customerServiceSession);

                serverCustomerWebSocket.roleInfo.setBandingRoleSession(null);
                serverCustomerWebSocket.roleInfo.setState(StateEnum.AVAILABLE.getCode());

                busyServiceMap.remove(customerServiceSession);
                busyServiceList.remove(customerServiceSession);

                // 更新available客服池及在线客服数量
                availableServiceMap.put(customerServiceSession, serverCustomerWebSocket);
                availableServiceList.add(customerServiceSession);
                onlineServiceCount.getAndIncrement();

                try {
                    sendMessageTarget("客户已离开，即将分配新的客户", customerServiceSession);
                } catch (IOException e) {
                    log.warn("客户已离开");
                    e.printStackTrace();
                }

                //清除繁忙客户池
                busyCustomerMap.remove(roleInfo.getSession());
                busyCustomerList.remove(roleInfo.getSession());

                //立即为客服寻找等待客户
                searchOnlineCustomer(customerServiceSession, serverCustomerWebSocket);


            } else {

                // 直接释放客户等待池即可
                waitingCustomerList.remove(roleInfo.getSession());
                waitingCustomerMap.remove(roleInfo.getSession());

                onlineCustomerCount.getAndDecrement();
            }

        }

    }

    /**
     * 收到客户端消息后调用的方法
     * @Param: message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {

        System.out.println(roleInfo.getRole() + "发送了消息" + message);

        if (roleInfo.getState().equals(StateEnum.AVAILABLE.getCode())) {

            if (roleInfo.getRole().equals(RoleEnum.CUSTOMER_SERVICE.getCode())) {
                //给客服发送消息
                try {
                    sendMessageSelf("无客户连接，无法发送消息");
                } catch (IOException e) {
                    log.error("发消息失败！！！，请重试");
                    e.printStackTrace();
                }
            } else {
                //给客户发送消息
                try {
                    sendMessageSelf("目前无客服连接，请连接成功后再尝试发送消息");
                } catch (IOException e) {
                    log.error("发消息失败！！！，请重试");
                    e.printStackTrace();
                }
            }

        } else {
            try {
                sendMessageTarget(message, roleInfo.getBandingRoleSession());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * @Param: session
     * @Param: error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误，session：{}", session.getId());
        error.printStackTrace();
    }


    /**
     * 主动给用户的绑定对象推送消息
     */
    private void sendMessage(String message) throws IOException {

        Session targetSession = roleInfo.getBandingRoleSession();
        if (targetSession == null) {
            return;
        }
        targetSession.getBasicRemote().sendText(message);
    }

    /**
     * 主动给指定用户推送消息
     */
    private void sendMessageTarget(String message, Session session) throws IOException {

        if (session == null) {
            return;
        }
        session.getBasicRemote().sendText(message);
    }


    /**
     * 主动给目标用户自身推送消息
     */
    private void sendMessageSelf(String message) throws IOException {
        Session session = roleInfo.getSession();
        if (session == null) {
            return;
        }
        session.getBasicRemote().sendText(message);
    }

    /**
     * 寻找空闲客服
     * @Param: []
     * @return: void
     */
    private void searchOnlineService(Session session, WebSocketServerCustomerHashMap2 webSocket) {
        //2.2 是否存在空闲客服
        if (onlineServiceCount.get() < 1) {

            log.info("暂时没有空闲客服");

            //给客户发送消息：“无空闲客服，请等待”
            try {
                sendMessageTarget("暂时没有空闲客服，系统将尽快为您指派", session);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            //与客服绑定
            Session customerServiceSession = availableServiceList.get(0);
            WebSocketServerCustomerHashMap2 customerService = availableServiceMap.get(customerServiceSession);

            // 客户与客服相互绑定
            webSocket.roleInfo.setBandingRoleSession(customerService.roleInfo.getSession());
            customerService.roleInfo.setBandingRoleSession(webSocket.roleInfo.getSession());

            //更新客户和客服状态
            webSocket.roleInfo.setState(1);
            customerService.roleInfo.setState(1);

            //清除等待客户等待状态
            waitingCustomerList.remove(session);
            waitingCustomerMap.remove(session, webSocket);

            // 清除空闲客服池
            availableServiceMap.remove(customerServiceSession);
            availableServiceList.remove(customerServiceSession);
            onlineServiceCount.getAndDecrement();

            // 添加繁忙客服池
            busyServiceMap.put(customerServiceSession, customerService);
            busyServiceList.add(customerServiceSession);

            //添加繁忙客户池
            busyCustomerMap.put(session, webSocket);
            busyCustomerList.add(session);
            onlineCustomerCount.getAndDecrement();

            //绑定成功，发送提示消息
            try {
                if (webSocket.roleInfo.getRole().equals(RoleEnum.DRIVER.getCode())) {
                    sendMessageTarget("与司机id:"+webSocket.roleInfo.getId()+",姓名:"+webSocket.roleInfo.getRoleName()+"建立连接，开始工作",
                            customerServiceSession);
                } else {
                    sendMessageTarget("与货主id:"+webSocket.roleInfo.getId()+",昵称:"+webSocket.roleInfo.getRoleName()+"建立连接，开始工作",
                            customerServiceSession);
                }
                sendMessageTarget("与路路通官方客服建立连接，很高兴为您服务", session);
                log.info("与客服{}建立连接，准备进入服务",customerService.roleInfo.getSession().getId());
            } catch (IOException e) {
                log.error("websocket连接中断，请重试!");
                e.printStackTrace();
            }
        }
    }

    /**
     * 寻找等待客户
     * @Param: []
     * @return: void
     */
    private void searchOnlineCustomer(Session session, WebSocketServerCustomerHashMap2 webSocket) {
        //2.1 寻找等待中的用户
        if (onlineCustomerCount.get() < 1) {

            log.info("没有等待的客户，将进入等待池");

            // 给客服发送消息，"无等待客户，进入等待状态"
            try {
                sendMessageTarget("无等待客户，进入等待状态", session);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            //2.1.2 绑定用户(寻找等待用户，直接用第一个即可)
            Session waitingCustomerSession = waitingCustomerList.get(0);
            WebSocketServerCustomerHashMap2 user = waitingCustomerMap.get(waitingCustomerSession);

            //客服与客户绑定session
            user.roleInfo.setBandingRoleSession(webSocket.roleInfo.getSession());
            webSocket.roleInfo.setBandingRoleSession(user.roleInfo.getSession());

            //更新客服状态和客户状态
            webSocket.roleInfo.setState(1);
            user.roleInfo.setState(1);

            //客户等待池
            waitingCustomerMap.remove(waitingCustomerSession);
            waitingCustomerList.remove(waitingCustomerSession);
            onlineCustomerCount.getAndDecrement();

            //清除客服等待池
            availableServiceMap.remove(session);
            availableServiceList.remove(session);
            onlineServiceCount.getAndDecrement();

            //添加客服繁忙池
            busyServiceMap.put(session, webSocket);
            busyServiceList.add(session);

            //添加客户繁忙池
            busyCustomerMap.put(waitingCustomerSession, user);
            busyCustomerList.add(waitingCustomerSession);

            //绑定成功，发送提示消息
            try {
                if (user.roleInfo.getRole().equals(RoleEnum.DRIVER.getCode())) {
                    sendMessageTarget("与司机id:"+user.roleInfo.getId()+",姓名:"+user.roleInfo.getRoleName()+"建立连接，准备为客户服务", session);
                } else {
                    sendMessageTarget("与货主id:"+user.roleInfo.getId()+",昵称:"+user.roleInfo.getRoleName()+"建立连接，准备为客户服务", session);
                }
                sendMessageTarget("与客服建立连接，很高兴为您服务", waitingCustomerSession);
                log.info("与用户{}建立连接，准备进入服务",user.roleInfo.getSession().getId());
            } catch (IOException e) {
                log.error("websocket连接中断，请重试!");
                e.printStackTrace();
            }
        }
    }


//    public RoleInfoResponse getRoleInfo() {
//
//    }
}

